package com.seed.application.service.set;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.RawQueryCondition;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowKey;
import com.seed.application.validator.UpdateValidator;
import com.seed.core.action.SetAction;
import com.seed.core.column.Column;
import com.seed.core.enumeration.PropertyType;
import com.seed.core.pojo.Const;
import com.seed.core.pojo.SetDTO;
import com.seed.core.pojo.seed.*;
import com.seed.core.query.pojo.Declared;
import com.seed.util.SeedKit;

import java.io.Serializable;
import java.util.*;

public class SetService {
    private final Seed seed;
    private final SetAction action;
    private final String declare;

    public SetService(Seed seed, SetAction action) {
        this.seed = seed;
        this.action = action;
        this.declare = getInferDeclare();
    }

    public boolean exec(SetDTO dto) {
        String id = dto.getId();
        String value = dto.getValue();
        if(StrUtil.isBlank(value)) value = null;

        Declared declared = SeedKit.getDeclared(seed, declare);
        PropertyType propertyType = declared.getType();
        switch (propertyType) {
            case column -> setForColumn(id, value);
            case belongsTo -> setForBelongsTo(id, value);
            case hasOne -> setForHasOne(id, value);
            case hasMany -> setForHasMany(id, value);
            case habtm -> setForHabtm(id, value);
            default -> throw new RuntimeException("列名不支持【空】或【*】配置");
        }

        return true;
    }

    public boolean execBatch(List<SetDTO> dtos) {
        throw new RuntimeException("TODO");
    }

    private void setForColumn(Serializable id, String value) {
        String tableName = seed.getTableName();
        Row row = DbChain.table(tableName).select(Const.ID, declare).where(Const.ID + " = ?", id).one();
        if(row == null) throw new RuntimeException("数据不存在");

        if(StrUtil.equals(value, row.getString(declare))) return;

        Declared declared = SeedKit.getDeclared(seed, declare);
        Column column = (Column) declared.getProperty();
        Row data = new Row().set(Const.ID, id).set(StrUtil.toCamelCase(declare), value);
        UpdateValidator.validate(seed, column, data);
        DbChain.table(tableName)
                .set(declare, value)
                .where(Const.ID + " = ?", id)
                .update();
    }

    private void setForHasOne(Serializable id, String declareValue) {
        String tableName = seed.getTableName();
        Declared declared = SeedKit.getDeclared(seed, declare);

//                TODO
        HasOnes hasOne = (HasOnes) declared.getProperty();
        Seed targetSeed = hasOne.targetSeed();
        String inferSelfColumn = hasOne.inferSelfColumn();
        String inferTargetColumn = hasOne.inferTargetColumn();
        String targetTableName = targetSeed.getTableName();

        Row row = DbChain.table(tableName).select(inferSelfColumn).where(Const.ID + " = ?", id).one();
        if(row == null) throw new RuntimeException("数据不存在");

        Column refColumn = getRefColumn(targetSeed, inferTargetColumn);
        if(refColumn == null) throw new RuntimeException("不能接管目标列");

        Object selfValue = row.get(inferSelfColumn);
        if(selfValue == null) throw new RuntimeException(String.format("【%s】值为空，不合法", inferSelfColumn));

        DbChain.table(targetTableName).where(inferTargetColumn + " = ?", selfValue).remove();
        if(StrUtil.isBlank(declareValue)) return;

        Row ret = Row.ofKey(RowKey.FLEX_ID);
        ret.set(inferTargetColumn, selfValue);
        ret.set(refColumn.getName(), declareValue);
        Db.insert(targetTableName, ret);
    }

    private void setForBelongsTo(Serializable id, String value) {
        String tableName = seed.getTableName();
        Declared declared = SeedKit.getDeclared(seed, declare);
        BelongsTos belongsTo = (BelongsTos) declared.getProperty();
        String inferSelfColumn = belongsTo.inferSelfColumn();
        Row row = DbChain.table(tableName).select(Const.ID, inferSelfColumn).where(Const.ID + " = ?", id).one();
        if(row == null) throw new RuntimeException("数据不存在");

        if(StrUtil.equals(value, row.getString(inferSelfColumn))) return;
        DbChain.table(tableName).set(inferSelfColumn, value).where(Const.ID + " = ?", id).update();
    }

    private void setForHasMany(Serializable id, String value) {
        String tableName = seed.getTableName();
        Declared declared = SeedKit.getDeclared(seed, declare);
        HasManys hasMany = (HasManys) declared.getProperty();
        Map<String, Object> extra = hasMany.getExtra();

        Seed targetSeed = hasMany.targetSeed();
        String inferSelfColumn = hasMany.inferSelfColumn();
        String inferTargetColumn = hasMany.inferTargetColumn();
        String targetTableName = targetSeed.getTableName();

        Row row = DbChain.table(tableName).select(inferSelfColumn).where(Const.ID + " = ?", id).one();
        if(row == null) throw new RuntimeException("数据不存在");

        Column refColumn = getRefColumn(targetSeed, inferTargetColumn);
        Object selfValue = row.get(inferSelfColumn);
        if(selfValue == null) throw new RuntimeException(String.format("【%s】值为空，不合法", inferSelfColumn));

        QueryCondition condition = new RawQueryCondition(inferTargetColumn + " = ?", selfValue);
        if(extra != null) {
            for(Map.Entry<String, Object> entry: extra.entrySet()) {
                condition.and(entry.getKey() + " = ?", entry.getValue());
            }
        }
        DbChain.table(targetTableName).where(condition).remove();

        if(StrUtil.isBlank(value)) return;

        List<String> targetValues = Arrays.stream(value.split(",")).map(String::trim).distinct().toList();
        List<Row> adding = new ArrayList<>();
        for(String targetValue: targetValues) {
            Row ret = Row.ofKey(RowKey.FLEX_ID);
            ret.set(inferTargetColumn, selfValue);
            ret.set(refColumn.getName(), targetValue);
            if(extra != null) {
                for(Map.Entry<String, Object> entry: extra.entrySet()) {
                    ret.set(entry.getKey(), entry.getValue());
                }
            }
            adding.add(ret);
        }
        Db.insertBatch(targetTableName, adding);
    }

    private void setForHabtm(Serializable id, String value) {
        String tableName = seed.getTableName();
        Declared declared = SeedKit.getDeclared(seed, declare);
        Habtms habtm = (Habtms) declared.getProperty();
        Map<String, Object> extra = habtm.getExtra();

        String inferSelfColumn = habtm.inferSelfColumn();
        String inferThroughSelfColumn = habtm.inferThroughSelfColumn();
        String inferThroughTargetColumn = habtm.inferThroughTargetColumn();
        String throughTableName = habtm.throughTableName();
        Row row = DbChain.table(tableName).select(inferSelfColumn).where(Const.ID + " = ?", id).one();
        if(row == null) throw new RuntimeException("数据不存在");

        Object selfValue = row.get(inferSelfColumn);
        if(selfValue == null) throw new RuntimeException(String.format("【%s】值为空，不合法", inferSelfColumn));

        QueryCondition condition = new RawQueryCondition(inferThroughSelfColumn + " = ?", selfValue);
        if(extra != null) {
            for(Map.Entry<String, Object> entry: extra.entrySet()) {
                condition.and(entry.getKey() + " = ?", entry.getValue());
            }
        }
        DbChain.table(throughTableName).where(condition).remove();

        if(StrUtil.isBlank(value)) return;

        List<String> targetValues = Arrays.stream(value.split(",")).map(String::trim).distinct().toList();
        List<Row> adding = new ArrayList<>();
        for(String targetValue: targetValues) {
            Row ret = Row.ofKey(RowKey.FLEX_ID);
            ret.set(inferThroughSelfColumn, selfValue);
            ret.set(inferThroughTargetColumn, targetValue);
            if(extra != null) {
                for(Map.Entry<String, Object> entry: extra.entrySet()) {
                    ret.set(entry.getKey(), entry.getValue());
                }
            }
            adding.add(ret);
        }
        Db.insertBatch(throughTableName, adding);
    }

    private Column getRefColumn(Seed seed, String throughSelfColumn) {
        LinkedHashMap<String, Column> columns = seed.getColumns();
        if(columns == null || columns.isEmpty()) return null;

        List<Column> ret = new ArrayList<>();
        for(Map.Entry<String, Column> entry: columns.entrySet()) {
            String columnName = entry.getKey();
            if(List.of("id", "tenant_id", "created_at", "updated_at", "deleted_at", "version", throughSelfColumn).contains(columnName)) continue;

            ret.add(entry.getValue());
        }

        // 只有一列，并且是引用类型的列，说明这个表是一个关系表
//        List<Column> refColumns = ret.stream().filter(e -> e.getType() == ColumnType.REF).toList();
//        if(refColumns.size() != 1) throw new RuntimeException("多个引用列产生歧义，不能接管目标列");

//        return refColumns.get(0);
        throw new RuntimeException("TODO");
    }

    private String getInferDeclare() {
        String declare = action.getDeclare();
        if(StrUtil.isNotBlank(declare)) return StrUtil.toUnderlineCase(declare);

        String actionName = action.getName();
        if(actionName.startsWith("get") || actionName.startsWith("set")) {
            actionName = StrUtil.lowerFirst(actionName.substring(3));
        }

        String columnOrCross = StrUtil.toUnderlineCase(actionName);
        if(seed.getColumns().containsKey(columnOrCross) || seed.getRelations().containsKey(columnOrCross)) {
            return columnOrCross;
        }

        throw new RuntimeException(String.format("请为【%s】配置【declare】", actionName));
    }
}
